package data;

import model.SDP;
import java.text.DateFormat;
import java.util.Date;
import java.util.Vector;
import model.Client;
import sip.VoIPserver;

/**
 * Class that process all the data, input from HTML client, audio file management, etc
 * @author lailiaidi
 */
public class DataHandler {

    //private SIPSpeaker obSIPSpeaker;
    private ConfigManager obConfigManager;
    private Audio obDefaultVoice, obCurrentVoice;
    private Vector obVectorClient;// Master data, contains all the client record
    private Vector obVectorRTPTransaction;// Contains all the client that currently have RTP transaction  with the server
    private boolean isRTPTransaction;
    private boolean isUsedTempData = false;
    private String obStrTempData;

    public DataHandler(String obStrConfigPath) {
        //this.obSIPSpeaker = obSIPSpeaker;
        this.obConfigManager = new ConfigManager(obStrConfigPath);
        this.obVectorClient = new Vector();
        this.obVectorRTPTransaction = new Vector();
        this.isRTPTransaction = isDelete = false;
        this.obCurrentVoice = null;

        this.obStrTempData = obConfigManager.getCurrentMessage();
        if (this.obStrTempData == null || this.obStrTempData.length() == 0) {
            this.obStrTempData = obConfigManager.getDefaultMessage();//HARDCODE_CurrentMessageText;
        }
        //Create the default audio file, in case the file is deleted.
        setDefaultVoice();
        //If User already save message before, create the audio file, in case the file is deleted.
        if (isAnyCurrentMessage()) {
            setCurrentVoice();
        }
    }

    public ConfigManager getConfiguration() {
        return obConfigManager;
    }

    /*
     * Set default Audio data, this is only called at the first time application run
     */
    private void setDefaultVoice() {
        String obStrTemp = obConfigManager.getDefaultVoiceName();
        if (obStrTemp.contains(".wav") || obStrTemp.contains(".WAV")) {
            obStrTemp = obStrTemp.toLowerCase();
            obStrTemp = obStrTemp.substring(0, obStrTemp.indexOf(".wav"));
        }
        this.obDefaultVoice = new Audio(obStrTemp, obConfigManager.getDefaultMessage());
        obDefaultVoice.createVoice();
        System.out.println("INFO: Default Voice is Created");
    }

    /* Get default Audio data */
    public String getDefaultVoice() {
        return obConfigManager.getDefaultVoiceName();
    }

    private synchronized void deleteCurrentVoice() {
        /*
         * Update the current voice data in the configuration file with the  blank
         */
        obConfigManager.setCurrentVoiceName("");
        obConfigManager.setCurrentVoiceDate("");
        obConfigManager.setCurrentMessage("");

        /* Delete the current voice file and set the object as null */
        this.obCurrentVoice.deleteVoice();
        this.obCurrentVoice = null;
    }

    private void prepareCurrentVoice(String obStrTime) {
        /*
         * Update the current message with the new submitted message The newest
         * submitted message might be different with what the client has been
         * submitted. Because another new client has changed the message after it is
         * changed by the previous client.
         */
        obConfigManager.setCurrentMessage(getTempData());
        /* Update the time when the message is submitted */
        obConfigManager.setCurrentVoiceDate(obStrTime);
        /* Update the current voice name with the new submitted message */
        obConfigManager.setCurrentVoiceName(obConfigManager.HARDCODE_CurrentVoiceName);
        setCurrentVoice();
    }

    /* Set new voice data based on user input */
    private synchronized void setCurrentVoice() {
        /* Make new Audio file */
        String obStrTemp = obConfigManager.getCurrentVoiceName();
        if (obStrTemp == null) {
            obStrTemp = obConfigManager.HARDCODE_CurrentVoiceName;
        }
        if (obStrTemp.contains(".wav") || obStrTemp.contains(".WAV")) {
            obStrTemp = obStrTemp.toLowerCase();
            obStrTemp = obStrTemp.substring(0, obStrTemp.indexOf(".wav"));
        }
        this.obCurrentVoice = new Audio(obStrTemp, obConfigManager.getCurrentMessage()
                + ". " + obConfigManager.getCurrentVoiceDate());
        obCurrentVoice.createVoice();
        System.out.println("INFO: Current Voice is Created");
    }

    /* Get current Audio data */
    public String getVoiceName() {
        String obStrTemp = "";
        if (isAnyCurrentMessage()) {
            obStrTemp = obConfigManager.getCurrentVoiceName();
        } else {
            obStrTemp = obConfigManager.getDefaultVoiceName();
        }

        if (!obStrTemp.contains(".wav")) {
            obStrTemp += ".wav";
        }
        return obStrTemp;
    }

    /* Method for Handling Checking for current message processing, exp deletion, accessing, etc */
    public boolean isAnyCurrentMessage() {
        if ((obConfigManager.getCurrentVoiceName() == null || obConfigManager.getCurrentVoiceName().length() == 0)
                || (obConfigManager.getCurrentMessage() == null || obConfigManager.getCurrentMessage().length() == 0)) {
            // Means no current message / voice data
            return false;
        }
        return true;
    }

    /* Method for Handling HTML client's data */
    public boolean isValidHTTPData(String emailData) {
        if (getHTTPData(emailData) != null && getHTTPData(emailData).length() > 0) {
            return true;
        }
        return false;
    }

    /* This method is used to decode the data from the web interface */
    public String getHTTPData(String data) {
        int x = data.indexOf("new");
        int y = data.indexOf("&btnSubmit");
        return data.substring(x + 4, y);
    }

    public String getNoMessageText() {
        return obConfigManager.getDefaultMessage();//HARDCODE_CurrentMessageText;
    }

    /* Update new message data based on user input */
    public void setTempData(String obStrTempData) {
        if (obStrTempData.length() == 0) {
            this.obStrTempData = obStrTempData;
            isDelete = true;
        } else {
            this.obStrTempData = getHTTPData(obStrTempData);
            isDelete = false;
        }
    }

    public String getTempData() {
        return obStrTempData;
    }
    private boolean isDelete;

    public synchronized boolean setCurrentMessage(String obStrNewMessage, Thread obThread) {
        /*  Get the time when the message is submitted */
        String obStrTime = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT).
                format(new Date());

        Thread obWaitThread = new Thread() {

            @Override
            public void run() {
                //Wait until RTP transaction is done, then we can update the audio data (change or delete)
                isUsedTempData = true;
                while (getRTPTransaction()) {
                    try {
                        Thread.currentThread().sleep(100);
                    } catch (InterruptedException ie) {
                        System.err.println("ERROR: Fail on Transision state 1");
                    } catch (Exception e) {
                        System.err.println("ERROR: Fail on Transision state 2");
                    }
                }
                try {
                    Thread.currentThread().stop();
                } catch (Exception e) {
                }
            }
        };

        /* Start the VoIP server */
        obWaitThread.start();
        isUsedTempData = false;
        if (isDelete) {
            deleteCurrentVoice();
        } else {
            prepareCurrentVoice(obStrTime);
        }
        return true;
    }

    /* Method for Handling VOIP client's data */
    public Client getClient(String obStrClientAddress, int obIntClientPort) {
        if (!obVectorClient.isEmpty()) {
            for (int i = 0; i < obVectorClient.size(); i++) {
                Client obClient = (Client) obVectorClient.get(i);
                // Check if this client is already served or not
                if (obClient.getAddress().equals(obStrClientAddress)
                        && obClient.getPort() == obIntClientPort) {
                    return obClient;
                }
            }
        } else {
            return null;
        }
        return null;
    }

    private int getClientIndex(Client obClient) {
        if (!obVectorClient.isEmpty()) {
            for (int i = 0; i < obVectorClient.size(); i++) {
                Client obClientHash = (Client) obVectorClient.get(i);
                /* Check if this client is already served or not */
                if (obClient.equals(obClientHash)) {
                    return i;
                }
            }
        } else {
            return -1;
        }
        return -1;
    }

    /* Add the data of this client to the Vector */
    public synchronized void addClient(String obStrClientAddress,
            int obIntClientPort) {
        if (getClient(obStrClientAddress, obIntClientPort) == null) {
            Client obClient = new Client(obStrClientAddress, obIntClientPort, VoIPserver.START_STAGE, null);
            obVectorClient.add(obClient);
        }
    }

    public synchronized void removeClient(String obStrClientAddress, int obIntClientPort) {
        Client obClient = getClient(obStrClientAddress, obIntClientPort);
        if (obClient != null) {
            int index = getClientIndex(obClient);
            if (index > -1) {
                obVectorClient.removeElementAt(index);
                /* If this client did RTP transaction before, then remove it  from the vector */
                if (obVectorRTPTransaction.contains(obClient)) {
                    obVectorRTPTransaction.remove(obClient);
                }
            }
        }
        updateRTPTransaction();
    }

    public synchronized void updateStage(String obStrClientAddress,
            int obIntClientPort, int obIntNewStage) {
        this.updateStage(obStrClientAddress, obIntClientPort, obIntNewStage, null);
    }

    public synchronized void updateStage(String obStrClientAddress,
            int obIntClientPort, int obIntNewStage, SDP obSDP) {
        Client obClient = getClient(obStrClientAddress, obIntClientPort);
        if (getClient(obStrClientAddress, obIntClientPort) != null) {
            obClient.setStage(obIntNewStage);
            int obIntVectorIndex = getClientIndex(obClient);
            obVectorClient.setElementAt(obClient, obIntVectorIndex);
            /* Update the RTP transaction flag */
            if (obIntNewStage == VoIPserver.ACK_STAGE) {
                obVectorRTPTransaction.add(obClient);
            } else {
                if (obIntNewStage == VoIPserver.INVITE_STAGE && obSDP != null) {
                    setSDP(obStrClientAddress, obIntClientPort, obSDP);
                }
                /* If this client did RTP transaction before, then remove it  from the vector  */
                if (obVectorRTPTransaction.contains(obClient)) {
                    obVectorRTPTransaction.remove(obClient);
                }
            }
            updateRTPTransaction();
        }
    }

    public int getStage(String obStrClientAddress, int obIntClientPort) {
        Client obClient = getClient(obStrClientAddress, obIntClientPort);
        if (obClient != null) {
            return obClient.getStage();
        }
        return -1;
    }

    public synchronized void setSDP(String obStrClientAddress,
            int obIntClientPort, SDP obSDP) {
        Client obClient = getClient(obStrClientAddress, obIntClientPort);
        if (obClient != null) {
            obClient.setSDP(obSDP);
            int obIntVectorIndex = getClientIndex(obClient);
            obVectorClient.setElementAt(obClient, obIntVectorIndex);
        }
    }

    public SDP getSDP(String obStrClientAddress, int obIntClientPort) {
        Client obClient = getClient(obStrClientAddress, obIntClientPort);
        if (obClient != null) {
            return obClient.getSDP();
        }
        return null;
    }

    public synchronized void updateRTPTransaction() {
        if (obVectorClient.isEmpty()) {
            /* Means no client is doing RTP transaction with the server now */
            this.isRTPTransaction = false;
        } else {
            /* Means there is at least 1 client is doing RTP transaction with  the server now */
            this.isRTPTransaction = true;
        }
    }

    public synchronized boolean getRTPTransaction() {
        return isRTPTransaction;
    }
}// Class end

